﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace CPPEI.WebApiTool.Utils
{
    public static class AssemblyUtil
    {
        public static DModule Get(string assemblyFile)
        {
            Assembly assembly = null;
            try
            { 
                assembly = Assembly.Load(File.ReadAllBytes(assemblyFile));
            }
            catch(Exception ex)
            {
                return null;
            }

            DModule module = BuildModule(assembly);

            if(module == null)
            {
                return null;
            }

            GetActions(assembly, module);

            return module;
        }

        private static DModule BuildModule(Assembly assembly)
        {
            Type[] types = null;
            
            try
            {
                types =  assembly.GetTypes();
            }
            catch(Exception ex)
            {
            }

            if(types == null)
            {
                return null;
            }

            foreach (var type in types)
            {
                if(type.IsSubclassOf(typeof(CPPEI.WebApi.BaseModule)))
                {
                    CPPEI.WebApi.ModuleAttribute moduleAttribute = type.GetCustomAttribute<CPPEI.WebApi.ModuleAttribute>();

                    if(moduleAttribute!=null)
                    {
                        DModule module = new DModule(moduleAttribute.Name);
                        module.Assembly = assembly;
                        return module;
                    }
                }
            }

            return null;
        }


        private static void GetActions(Assembly assembly, DModule module)
        {
            Type[] types = assembly.GetTypes();

            foreach (var type in types)
            {
                if (type.IsSubclassOf(typeof(CPPEI.WebApi.BaseAction)))
                {
                    CPPEI.WebApi.ActionAttribute actionAttribute = type.GetCustomAttribute<CPPEI.WebApi.ActionAttribute>();
                    if(actionAttribute!=null)
                    {
                        DScenario scenario = module.GetOrCreate(actionAttribute.Scenario);
                        DAction action = new DAction(type, actionAttribute.Name, actionAttribute.HttpMethod, actionAttribute.IsAnonymous, string.Empty);
                        scenario.Actions.Add(action);

                        Type[] gtypes = type.BaseType.GetGenericArguments();
                        FillAction(action, gtypes[0], gtypes[1]);
                    }
                }
            }
        }

        private static void FillAction(DAction action, Type paramType, Type resultType)
        {
            DResult param = action.AddParameter(new DResult(paramType, "", string.Empty));
            FillParams(action, param, paramType);

            DResult result = action.AddResult(new DResult(resultType, "Data", string.Empty));
            FillResults(action, result, resultType);
        }

        #region params

        private static void FillParams(DAction action, DResult pResult, Type resultType)
        {
            #region property
            PropertyInfo[] props = resultType.GetProperties();
            foreach (var prop in props)
            {
                if (prop.PropertyType.IsClass && prop.PropertyType != typeof(string))
                {
                    #region class property
                    if (prop.PropertyType == typeof(System.Web.HttpPostedFile))
                    {
                        pResult.AddItem(new DResultItem(prop.PropertyType, prop.Name, string.Empty));
                    }
                    else
                    {
                        if (prop.PropertyType.IsSubclassOf(typeof(Array)))
                        {
                            #region array
                            Type pgType = prop.PropertyType.GetElementType();
                            // 先生成项目
                            pResult.AddItem(new DResultItem(string.Format("{0}[]", pgType.Name),
                                prop.PropertyType,
                                prop.Name,
                                string.Empty));

                            if (pgType.IsClass && pgType != typeof(string))
                            {
                                if (action.GetResult(pgType) == null)
                                {
                                    // 集合类型的属性，获取子项目类型
                                    DResult result = action.AddParameter(new DResult(pgType, pgType.Name, string.Empty));

                                    FillParams(action, result, pgType);
                                }
                            }
                            #endregion
                        }
                        else if (typeof(IEnumerable).IsAssignableFrom(prop.PropertyType))
                        {
                            #region collection
                            Type[] pgTypes = prop.PropertyType.GetGenericArguments();
                            if (pgTypes.Length > 0)
                            {
                                Type pgType = pgTypes[0];
                                // 先生成项目
                                pResult.AddItem(new DResultItem(string.Format("{0}[]", pgType.Name),
                                    prop.PropertyType,
                                    prop.Name,
                                    string.Empty));

                                if (pgType.IsClass && pgType != typeof(string))
                                {
                                    if (action.GetResult(pgType) == null)
                                    {
                                        // 集合类型的属性，获取子项目类型
                                        DResult result = action.AddParameter(new DResult(pgType, pgType.Name, string.Empty));

                                        FillParams(action, result, pgType);
                                    }
                                }
                            }
                            #endregion
                        }
                        else
                        {
                            pResult.AddItem(new DResultItem(prop.PropertyType, prop.Name, string.Empty));

                            if (action.GetResult(prop.PropertyType) == null)
                            {
                                DResult result = action.AddParameter(new DResult(prop.PropertyType, prop.PropertyType.Name, string.Empty));

                                FillParams(action, result, prop.PropertyType);
                            }
                        }
                    }
                    #endregion
                }
                else
                {
                    pResult.AddItem(new DResultItem(prop.PropertyType,
                        prop.Name, string.Empty));
                }
            }
            #endregion

            #region fields
            FieldInfo[] fields = resultType.GetFields();
            foreach (var field in fields)
            {
                if (field.FieldType.IsClass && field.FieldType != typeof(string))
                {
                    if (field.FieldType == typeof(System.Web.HttpPostedFile))
                    {
                        pResult.AddItem(new DResultItem(field.FieldType, field.Name, string.Empty));
                    }
                    else
                    {
                        if (typeof(IEnumerable).IsAssignableFrom(field.FieldType))
                        {
                            #region collection
                            Type[] fgTypes = field.FieldType.GetGenericArguments();
                            if (fgTypes.Length > 0)
                            {
                                Type fgType = fgTypes[0];

                                pResult.AddItem(new DResultItem(string.Format("{0}[]", fgType.Name),
                                    field.FieldType,
                                    field.Name,
                                    string.Empty));

                                if (fgType.IsClass && fgType != typeof(string))
                                {
                                    if (action.GetResult(fgType) == null)
                                    {
                                        DResult result = action.AddParameter(new DResult(fgType, fgType.Name, string.Empty));

                                        FillParams(action, result, fgType);
                                    }
                                }
                            }
                            #endregion
                        }
                        else
                        {
                            pResult.AddItem(new DResultItem(field.FieldType, field.Name, string.Empty));

                            if (field.FieldType.IsClass && field.FieldType != typeof(string))
                            {
                                if (action.GetResult(field.FieldType) == null)
                                {
                                    DResult result = action.AddParameter(new DResult(field.FieldType, field.FieldType.Name, string.Empty));

                                    FillParams(action, result, field.FieldType);
                                }
                            }
                        }
                    }
                }
                else
                {
                    pResult.AddItem(new DResultItem(field.FieldType, field.Name, string.Empty));
                }
            }
            #endregion
        }

        #endregion

        #region result
        private static void FillResults(DAction action, DResult pResult, Type resultType)
        {
            #region property
            PropertyInfo[] props = resultType.GetProperties();
            foreach (var prop in props)
            {
                if (prop.PropertyType.IsClass && prop.PropertyType != typeof(string))
                {
                    #region class property
                    if (typeof(IEnumerable).IsAssignableFrom(prop.PropertyType))
                    {
                        #region collection
                        Type[] pgTypes = prop.PropertyType.GetGenericArguments();
                        if (pgTypes.Length > 0)
                        {
                            Type pgType = pgTypes[0];
                            // 先生成项目
                            pResult.AddItem(new DResultItem(string.Format("{0}[]", pgType.Name),
                                prop.PropertyType,
                                prop.Name,
                                string.Empty));

                            if (pgType.IsClass && pgType != typeof(string))
                            {
                                if (action.GetResult(pgType) == null)
                                {
                                    // 集合类型的属性，获取子项目类型
                                    DResult result = action.AddResult(new DResult(pgType, pgType.Name, string.Empty));

                                    FillResults(action, result, pgType);
                                }
                            }
                        }
                        #endregion
                    }
                    else
                    {
                        pResult.AddItem(new DResultItem(prop.PropertyType, prop.Name, string.Empty));

                        if (action.GetResult(prop.PropertyType) == null)
                        {
                            DResult result = action.AddResult(new DResult(prop.PropertyType, prop.PropertyType.Name, string.Empty));

                            FillResults(action, result, prop.PropertyType);
                        }
                    }
                    #endregion
                }
                else
                {
                    pResult.AddItem(new DResultItem(prop.PropertyType,
                        prop.Name,
                        string.Empty));
                }
            }
            #endregion

            #region fields
            FieldInfo[] fields = resultType.GetFields();
            foreach (var field in fields)
            {
                if (field.FieldType.IsClass && field.FieldType != typeof(string))
                {
                    if (typeof(IEnumerable).IsAssignableFrom(field.FieldType))
                    {
                        #region collection
                        Type[] fgTypes = field.FieldType.GetGenericArguments();
                        if (fgTypes.Length > 0)
                        {
                            Type fgType = fgTypes[0];

                            pResult.AddItem(new DResultItem(string.Format("{0}[]", fgType.Name),
                                field.FieldType,
                                field.Name,
                                string.Empty));

                            if (fgType.IsClass && fgType != typeof(string))
                            {
                                if (action.GetResult(fgType) == null)
                                {
                                    DResult result = action.AddResult(new DResult(fgType, fgType.Name, string.Empty));

                                    FillResults(action, result, fgType);
                                }
                            }
                        }
                        #endregion
                    }
                    else
                    {
                        pResult.AddItem(new DResultItem(field.FieldType, field.Name, string.Empty));

                        if (field.FieldType.IsClass && field.FieldType != typeof(string))
                        {
                            if (action.GetResult(field.FieldType) == null)
                            {
                                DResult result = action.AddResult(new DResult(field.FieldType, field.FieldType.Name, string.Empty));

                                FillResults(action, result, field.FieldType);
                            }
                        }
                    }
                }
                else
                {
                    pResult.AddItem(new DResultItem(field.FieldType, field.Name, string.Empty));
                }
            }
            #endregion
        }

        #endregion
    }
}
